/*
 * osc_io.c
 *
 *  Created on: Oct 28, 2012
 *      Author: andrey
 */

/**
 *
 * PC0 violet
 * PC1 grey
 * PC2 green
 * PC3 blue
 */

#include "osc_io.h"
#include "osc_state.h"

static void pwmpcb(PWMDriver *pwmp);
static void adc_callback(ADCDriver *adcp, adcsample_t *buffer, size_t n);

/* Depth of the conversion buffer, channels are sampled four times each.*/
#define ADC_GRP1_BUF_DEPTH      4

/*
 * ADC samples buffer.
 */
static adcsample_t samples[ADC_NUMBER_CHANNELS * ADC_GRP1_BUF_DEPTH];

/* 10kHz PWM clock frequency.   */
#define PWM_FREQ 50000
/* PWM period 1S (in ticks).    */
#define PWM_PERIOD 5

/*
 * PWM configuration structure.
 * Cyclic callback enabled, channels 1 and 4 enabled without callbacks,
 * the active state is a logic one.
 */
static PWMConfig pwmcfg = { PWM_FREQ, PWM_PERIOD, pwmpcb, {

{ PWM_OUTPUT_DISABLED, NULL }, { PWM_OUTPUT_DISABLED, NULL }, {
		PWM_OUTPUT_DISABLED, NULL }, { PWM_OUTPUT_DISABLED, NULL } },
/* HW dependent part.*/
0 };
/* Depth of the conversion buffer, channels are sampled four times each.*/
#define ADC_GRP1_BUF_DEPTH      4

/*
 * ADC samples buffer.
 */
static adcsample_t samples[ADC_NUMBER_CHANNELS * ADC_GRP1_BUF_DEPTH];

/*
 * ADC conversion group.
 * Mode:        Linear buffer, 4 samples of 2 channels, SW triggered.
 * Channels:    IN11   (48 cycles sample time)
 *              Sensor (192 cycles sample time)
 */
static const ADCConversionGroup adcgrpcfg =
		{ FALSE, ADC_NUMBER_CHANNELS, adc_callback, NULL,
		/* HW dependent part.*/
		0,   // cr1
				ADC_CR2_SWSTART, // cr2

				ADC_SMPR1_SMP_AN11(ADC_SAMPLE_56)
						| ADC_SMPR1_SMP_SENSOR(ADC_SAMPLE_144), // sample times for channels 10...18
				0, // In this field must be specified the sample times for channels 0...9

				ADC_SQR1_NUM_CH(ADC_NUMBER_CHANNELS), // Conversion group sequence 13...16 + sequence length

				0, // Conversion group sequence 7...12
				0
				| ADC_SQR3_SQ1_N(ADC_CHANNEL_IN10) /* PC0 violet*/
				| ADC_SQR3_SQ2_N(ADC_CHANNEL_IN12) /* PC2 blue */
				| ADC_SQR3_SQ3_N(ADC_CHANNEL_IN0)  /* PA0 green */
				| ADC_SQR3_SQ4_N(ADC_CHANNEL_IN2)  /* PA2 yellow */
				| ADC_SQR3_SQ5_N(ADC_CHANNEL_IN4)  /* PA4 orange */
				| ADC_SQR3_SQ6_N(ADC_CHANNEL_IN11) /* PC1 grey */

//				| ADC_SQR3_SQ6_N(ADC_CHANNEL_IN13) /* PC3 */
//				| ADC_SQR3_SQ2_N(ADC_CHANNEL_IN1)  /* PA1 */
//				| ADC_SQR3_SQ2_N(ADC_CHANNEL_IN3) /* PA3 */
//				| ADC_SQR3_SQ2_N(ADC_CHANNEL_IN5) /* PA5 */
		// Conversion group sequence 1...6
		};

static void pwmpcb(PWMDriver *pwmp) {

	(void) pwmp;

	/* Starts an asynchronous ADC conversion operation, the conversion
	 will be executed in parallel to the current PWM cycle and will
	 terminate before the next PWM cycle.*/chSysLockFromIsr()
	;
	adcStartConversionI(&ADCD1, &adcgrpcfg, samples, ADC_GRP1_BUF_DEPTH);
	chSysUnlockFromIsr();
}

adcsample_t getAdcValue(int index, adcsample_t *samples) {
	adcsample_t result = 0;
	int i;
	for (i = 0; i < ADC_GRP1_BUF_DEPTH; i++) {
		result += samples[index];
		index += ADC_NUMBER_CHANNELS;
	}
	return result / ADC_GRP1_BUF_DEPTH;
}

/*
 * ADC end conversion callback.
 * The PWM channels are reprogrammed using the latest ADC samples.
 * The latest samples are transmitted into a single SPI transaction.
 */
adcsample_t avg_ch0 = 0;
adcsample_t avg_ch1 = 1;
adcsample_t avg_ch2 = 2, avg_ch3 = 3;

volatile adc_callback_counter = 0;

int getAdc0() {
	return avg_ch0;
}

int getAdc1() {
	return avg_ch1;
}

int getAdc2() {
	return avg_ch2;
}

int getAdc3() {
	return avg_ch3;
}

void adc_callback(ADCDriver *adcp, adcsample_t *buffer, size_t n) {
	(void) buffer;
	(void) n;
	/* Note, only in the ADC_COMPLETE state because the ADC driver fires an
	 intermediate callback when the buffer is half full.*/
	if (adcp->state == ADC_COMPLETE) {
		/* Calculates the average values from the ADC samples.*/

		avg_ch0 = getAdcValue(0, samples);
		avg_ch1 = getAdcValue(1, samples);
		avg_ch2 = getAdcValue(2, samples);
		avg_ch3 = getAdcValue(3, samples);

		adc_state newState;
		newState.time = chTimeNow();
		int i;
		for (i = 0; i < ADC_NUMBER_CHANNELS; i++) {
			int value = getAdcValue(i, samples);
			newState.adc_data[i] = value;
		}

		adc_callback_counter++;

		chSysLockFromIsr()
		;
		addState(&newState);

		/* Changes the channels pulse width, the change will be effective
		 starting from the next cycle.*/
//		pwmEnableChannelI(&PWMD4, 0,
//				PWM_FRACTION_TO_WIDTH(&PWMD4, 4096, avg_ch1));
//		pwmEnableChannelI(&PWMD4, 3,
//				PWM_FRACTION_TO_WIDTH(&PWMD4, 4096, avg_ch2));
		/* SPI slave selection and transmission start.*/
//		spiSelectI(&SPID2);
		//spiStartSendI(&SPID2, ADC_GRP1_NUM_CHANNELS * ADC_GRP1_BUF_DEPTH, samples);
		chSysUnlockFromIsr();
	}
}

void initMyAdc() {
	adc_callback_counter = 0;

	/*
	 * Initializes the ADC driver 1 and enable the thermal sensor.
	 * The pin PC0 on the port GPIOC is programmed as analog input.
	 */
	adcStart(&ADCD1, NULL );
//	adcSTM32EnableTSVREFE();
	// PC0, PC1, PC2 & PC3
	palSetPadMode(GPIOC, ADC_PC0, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOC, ADC_PC1, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOC, ADC_PC2, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOC, ADC_PC3, PAL_MODE_INPUT_ANALOG);

	// PA0, PA1
	palSetPadMode(GPIOA, ADC_PA0, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOA, ADC_PA1, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOA, ADC_PA2, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOA, ADC_PA3, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOA, ADC_PA4, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOA, ADC_PA5, PAL_MODE_INPUT_ANALOG);

	/*
	 * Initializes the PWM driver 4, routes the TIM4 outputs to the board LEDs.
	 */
	pwmStart(&PWMD4, &pwmcfg);
}
